package com.ksyun.campus.metaserver.entity.meta.tree;

import com.alibaba.fastjson2.JSON;
import com.ksyun.campus.metaserver.entity.meta.FileType;
import com.ksyun.campus.metaserver.entity.meta.StatInfo;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * 根据fileSystemName划分管理不同的fileTree
 */
@Component
public class FileTreeManager {
    @Resource
    private StringRedisTemplate stringRedisTemplate;


    /**
     * 添加文件到指定的文件系统中
     *
     * @param fileSystemName 命名空间
     * @param path           文件路径
     * @param statInfo       元数据
     * @return true 如果文件成功添加，false 如果父目录不存在
     */
    public boolean addFile(String fileSystemName, String path, StatInfo statInfo) {
        // 检查父级目录是否存在
        String parentPath = getParentPath(path);
        if (parentPath != null && !pathExists(fileSystemName, parentPath)) {
            // 父级目录不存在，拒绝创建
            if (!parentPath.equals("/")) {
                return false;
            }
            // 否则是根目录，默认创建
            stringRedisTemplate.opsForHash().put(
                    fileSystemName,
                    "/",
                    JSON.toJSONString(StatInfo.builder().path("/").type(FileType.Directory).build())
            );
        }

        // 父级目录存在，继续创建文件
        String value = JSON.toJSONString(statInfo);
        stringRedisTemplate.opsForHash().put(fileSystemName, path, value);
        return true;
    }

    /**
     * 获取父级目录路径
     *
     * @param path 文件路径
     * @return 父级目录路径，或者 null 如果该路径是根路径
     */
    private String getParentPath(String path) {
        int lastSlashIndex = path.lastIndexOf('/');
        if (lastSlashIndex > 0) {
            return path.substring(0, lastSlashIndex);
        }
        // 父级目录是根 /
        if (lastSlashIndex == 0) {
            return "/";
        }
        return null; // 根路径没有父级目录
    }

    /**
     * 递归删除指定路径及其子路径的所有节点
     *
     * @param fileSystemName 文件系统名称
     * @param path           文件或目录路径
     * @return true 删除成功，false 删除失败
     */
    public boolean deleteFile(String fileSystemName, String path) {

        // Step 1: 收集需要删除的键
        List<String> keysToDelete = new ArrayList<>();
        try (Cursor<Map.Entry<Object, Object>> cursor = stringRedisTemplate.opsForHash().scan(fileSystemName, ScanOptions.scanOptions().match(path + "*").build())) {
            while (cursor.hasNext()) {
                Map.Entry<Object, Object> entry = cursor.next();
                keysToDelete.add((String) entry.getKey());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

        // Step 2: 使用管道批量删除
        stringRedisTemplate.executePipelined((RedisCallback<?>) (connection) -> {
            for (String hashKey : keysToDelete) {
                connection.hDel(fileSystemName.getBytes(), hashKey.getBytes());
            }
            return null;
        });

        return !keysToDelete.isEmpty();
    }

    /**
     * 更新指定文件系统中的文件元数据
     *
     * @param fileSystemName 命名空间
     * @param path           文件路径
     * @param statInfo       元数据
     * @return 结果：true 更新成功；false 更新失败（路径不存在）
     */
    public boolean updateFile(String fileSystemName, String path, StatInfo statInfo) {
        // 将 StatInfo 对象序列化为 JSON 字符串
        // 使用 HSET 更新文件元数据
        return Boolean.TRUE.equals(stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
            // 将 StatInfo 对象序列化为 JSON 字符串
            String value = JSON.toJSONString(statInfo);
            // 使用 HSET 更新文件元数据
            connection.hSet(fileSystemName.getBytes(), path.getBytes(), value.getBytes());
            return true;
        }));
    }

    /**
     * 查找指定路径的元数据
     *
     * @param fileSystemName 文件系统名称
     * @param path           文件或目录路径
     * @return StatInfo 如果存在返回元数据，否则返回null
     */
    public StatInfo findNode(String fileSystemName, String path) {
        String value = (String) stringRedisTemplate.opsForHash().get(fileSystemName, path);
        if (value != null) {
            return JSON.parseObject(value, StatInfo.class);
        }
        return null;
    }

    /**
     * 判断指定文件系统中的路径是否存在
     *
     * @param fileSystemName 命名空间
     * @param path           文件或目录路径
     * @return 结果：true 路径存在；false 路径不存在
     */
    public boolean pathExists(String fileSystemName, String path) {
        return stringRedisTemplate.opsForHash().hasKey(fileSystemName, path);
    }

    /**
     * 打印指定文件系统的文件树
     *
     * @param fileSystemName 命名空间
     */
    public void printFileTree(String fileSystemName) {
        System.out.println("FileSystemName: " + fileSystemName);
        printSubTree(fileSystemName, "/", "");
    }

    private void printSubTree(String fileSystemName, String currentPath, String prefix) {
        String key = fileSystemName;

        // 用于存储当前层的子节点
        Map<String, String> subNodes = new TreeMap<>();

        try (Cursor<Map.Entry<Object, Object>> cursor = stringRedisTemplate.opsForHash().scan(key, ScanOptions.scanOptions().match(currentPath + "*").build())) {
            while (cursor.hasNext()) {
                Map.Entry<Object, Object> entry = cursor.next();
                String fullPath = (String) entry.getKey();
                String relativePath = fullPath.substring(currentPath.length());
                String[] parts = relativePath.split("/");

                // 仅添加当前层级的节点
                if (parts.length == 1 && !fullPath.equals(currentPath)) {
                    subNodes.put(fullPath, (String) entry.getValue());
                }
            }

            int index = 0;
            int size = subNodes.size();
            for (Map.Entry<String, String> entry : subNodes.entrySet()) {
                boolean isLast = ++index == size;
                String nodeName = entry.getKey().substring(currentPath.length());
                String nodePrefix = isLast ? "└── " : "├── ";
                System.out.println(prefix + nodePrefix + nodeName);

                // 如果是目录，递归打印子树
                if (entry.getValue().contains("\"type\":\"Directory\"")) {
                    String newPrefix = prefix + (isLast ? "    " : "│   ");
                    printSubTree(fileSystemName, entry.getKey() + "/", newPrefix);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
